package com.iwuyc.tools.commons.util.math;

import com.iwuyc.tools.commons.util.string.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.Optional;

/**
 * N十六进制转换
 *
 * @author Neil
 */
@Slf4j
public class HexUtils {


    /**
     * 进制前缀长度
     */
    private static final int PREFIX_LENGTH = 2;

    private HexUtils() {
    }

    /**
     * 将数字转换成指定的进制(16、32、64)
     *
     * @param number  待转换的数字
     * @param hexEnum 目标进制(16、32、64)
     * @return 转换后的进制字符串(前缀分别为 : 16 : 0x ; 32 : 1x ; 64 : 2x)
     */
    public static String toHexString(final long number, HexEnum hexEnum) {
        StringBuilder resultBuilder = new StringBuilder();
        long tempNumber = number;
        char[] hexChars = HexEnum.getHexChars();
        do {
            final int charIndex = (int) (tempNumber & hexEnum.getHexMasks());
            resultBuilder.append(hexChars[charIndex]);
            tempNumber = tempNumber >>> hexEnum.getMaskLength();
        } while (tempNumber != 0);
        String hexPrefix = hexEnum.getHexPrefix();
        return hexPrefix + resultBuilder.reverse();
    }

    /**
     * 将16、32、64进制格式的字符串转换为long类型的数字
     *
     * @param hexNumber 64进制数字
     * @return 转换后的数字
     */
    public static long fromHexString(final String hexNumber) {
        Optional<HexEnum> hexEnumOpt = hexEnum(hexNumber);
        if (!hexEnumOpt.isPresent()) {
            throw new NumberFormatException("未找到对应的进制信息。0x、1x、2x,分别表示16、32、64进制。");
        }
        HexEnum hexEnum = hexEnumOpt.get();
        long num = 0;
        int length = hexNumber.length();
        for (int i = 2; i < length; i++) {
            char numberChar = hexNumber.charAt(i);
            int indexByChar = HexEnum.char2Number(numberChar);
            num = num | indexByChar;
            if (i < length - 1) {
                num = num << hexEnum.getMaskLength();
            }
        }
        return num;
    }

    /**
     * 根据16、32、64进制的字符串，获取该字符串对应的进制枚举，如果前缀不为16、32、64格式的字符串，则返回 {@link Optional#empty()}
     *
     * @param hexNumber 16、32、64进制的字符串
     * @return hexNumber对应的 {@link HexEnum} 实例
     * @see HexEnum
     */
    public static Optional<HexEnum> hexEnum(String hexNumber) {
        if (StringUtils.length(hexNumber) < PREFIX_LENGTH) {
            return Optional.empty();
        }
        String hexPrefix = hexNumber.substring(0, 2);
        for (HexEnum item : HexEnum.values()) {
            if (Objects.equals(item.getHexPrefix(), hexPrefix)) {
                return Optional.of(item);
            }
        }
        return Optional.empty();
    }
}
